package com.kelvem.saas.workbanch.fsm.core;

import com.kelvem.saas.workbanch.fsm.entity.Entity;
import com.kelvem.saas.workbanch.fsm.model.EventNode;
import com.kelvem.saas.workbanch.ontology.address.model.AddressEntity;
import com.kelvem.saas.workbanch.ontology.piaoorder.model.PiaoOrderAddressEntity;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 *
 *
 * @author kelvem
 */
@Data
@Slf4j
public class EventContext {
    
    private static ThreadLocal<EventContextItem> localVar = new ThreadLocal<>();
    
    public static void init() {
        localVar.set(new EventContextItem());
    }
    
    public static void setEventNode(EventNode eventNode) {
        EventContextItem item = localVar.get();
        item.setEventNode(eventNode);
    }
    public static void setRequest(Object req) {
        EventContextItem item = localVar.get();
        item.setRequest(req);
    }
    public static void addContext(Entity entity) {
        EventContextItem buf = localVar.get();
        ContextItem item = buf.context;
        // clazzMap
        LinkedHashSet<Entity> clazzSet = item.clazzMap.get(entity.getClass());
        if (clazzSet == null) {
            clazzSet = new LinkedHashSet<>();
        }
        clazzSet.add(entity);
        item.clazzMap.put(entity.getClass(), clazzSet);
        // idMap
        Map<Long, LinkedHashSet<Entity>> idMapBuf = item.idMap.get(entity.getClass());
        if (idMapBuf == null) {
            idMapBuf = new HashMap<>();
        }
        LinkedHashSet<Entity> clazzSetBuf = idMapBuf.get(entity.getId());
        if (clazzSetBuf == null) {
            clazzSetBuf = new LinkedHashSet<>();
        }
        clazzSetBuf.add(entity);
        idMapBuf.put(entity.getId(), clazzSetBuf);
        item.idMap.put(entity.getClass(), idMapBuf);
        
    }
    
    public static void addContext(List<? extends Entity> list) {
        if (list == null || list.size() <= 0) {
            return;
        }
        for (Entity entity :
                list) {
            addContext(entity);
        }
    }
    
    public static void setResponse(Object resp) {
        EventContextItem item = localVar.get();
        item.setResponse(resp);
    }
    
    
    public static EventNode getEventNode() {
        return localVar.get().getEventNode();
    }
    public static Object getRequest() {
        return localVar.get().getRequest();
    }
    public static Object getResult() {
        return localVar.get().getResponse();
    }
    
    public static ContextItem getContext() {
        return localVar.get().getContext();
    }
    
    //-----------------------------------------------------------
    //        public
    //-----------------------------------------------------------
    
    public static <T extends Entity> T getEntity(Class<T> clazz) {
        Set<Entity> sets = localVar.get().getContext().clazzMap.get(clazz);
        if (sets == null) {
            return null;
        } else {
            for (Class<? extends Entity> entity:
                    localVar.get().getContext().clazzMap.keySet()) {
                if (clazz.isInstance(entity)) {
                    sets = localVar.get().getContext().clazzMap.get(entity);
                    return (T) sets.iterator().next();
                }
            }
            return (T) sets.iterator().next();
        }
    }
    
    public static <T extends Entity> List<T> getEntityList(Class<T> clazz) {
        Set<Entity> list = localVar.get().getContext().clazzMap.get(clazz);
        if (list == null) {
            for (Class<? extends Entity> entity:
                    localVar.get().getContext().clazzMap.keySet()) {
                if (clazz.isAssignableFrom(entity)) {
                    list = localVar.get().getContext().clazzMap.get(entity);
                    return new ArrayList<T>((Set<T>)list);
                }
            }
        }
        return new ArrayList<T>((Set<T>)list);
    }
    public static <T extends Entity> List<T> getEntityList(Class<T> clazz, Long id) {
        Map<Long, LinkedHashSet<Entity>> map = localVar.get().getContext().idMap.get(clazz);
        return new ArrayList<T>((LinkedHashSet<T>)map.get(id));
    }
    public static List<Entity> getEntityList() {
        List<Entity> list = new ArrayList<>();
        Map<Class<? extends Entity>, LinkedHashSet<Entity>> clazzMap = localVar.get().getContext().clazzMap;
        for (Set<Entity> entitySet:
                clazzMap.values()) {
            for (Entity entity :
                    entitySet) {
                list.add(entity);
            }
        }
        return list;
    }
    
    public static void print() {
        if (false) {
            return;
        }
        log.info(">>>>>>>>>>>>> EventContext start >>>>>>>>>>>>>");
        log.info("eventCode = " + localVar.get().getEventNode());
        log.info("request = " + localVar.get().getRequest());
        log.info("response = " + localVar.get().getResponse());
    
        // context
        Map<Class<? extends Entity>, Map<Long, LinkedHashSet<Entity>>> idMap = localVar.get().getContext().idMap;
        for (Class clazz:
                idMap.keySet()) {
            Map<Long, LinkedHashSet<Entity>> map = idMap.get(clazz);
            for (Set<Entity> entitySet:
                 map.values()) {
                for (Entity entity :
                        entitySet) {
                    log.info("context = [" + clazz.getSimpleName() + ", key=" + entity.getId() + "] " + entity);
                }
            }
        }
        log.info("<<<<<<<<<<<<< EventContext end   <<<<<<<<<<<<<");
        log.info("");
    }
    
    @Data
    private static class EventContextItem {
    
        private EventNode eventNode;
        private Object request;
        private ContextItem context = new ContextItem();
        private Object response;
    }
    
    @Data
    private static class ContextItem {
        private Map<Class<? extends Entity>, LinkedHashSet<Entity>> clazzMap = new LinkedHashMap<>();
        private Map<Class<? extends Entity>, Map<Long, LinkedHashSet<Entity>>> idMap = new LinkedHashMap<>();
    
    }
    
}
